/*
 * Properties.cc
 *
 * @date Mar 16, 2012
 * @author Erik Dietz-Laursonn, Tim Niggemann, III Phys. Inst. A, RWTH Aachen University
 * @copyright GNU General Public License v3.0
 */

#include "Properties.hh"

#include <boost/regex.hpp>
#include <boost/tokenizer.hpp>

#include <G4UnitsTable.hh>

#include <CLHEP/Units/SystemOfUnits.h>
#include <CLHEP/Units/PhysicalConstants.h>

#include <math.h>
#include <cmath>
#include <iostream>
#include <fstream>

Properties::Properties()
{
	// 匹配零个或多个空白字符
	std::string spacePattern = "[[:space:]]*";
	// 匹配由字母、数字、下划线或连字符组成的字符串
	std::string stringPattern = "([a-zA-Z0-9_-]+)";
	// 匹配一个可能带有正负号的小数或科学计数法表示的数字
	std::string numberPattern = "([+-]?[0-9]*[.]?[0-9]*[eE]?[+-]?[0-9]*)";
	// 匹配一个字符串或数字
	std::string stringOrNumberPattern = "([a-zA-Z0-9_-]+|[+-]?[0-9]*[.]?[0-9]*[eE]?[+-]?[0-9]*)";
	// 匹配一个由一个字符串和一个等号或冒号组成的关键字，关键字前后可以有空白字符
	std::string keyPattern = stringPattern + spacePattern + "[=:]{1}" + spacePattern;
	// 匹配一个由一个字母或百分号开头的单位，后面可以跟字母、数字和斜杠
	std::string unitPattern = spacePattern + "([1a-zA-Z%]{1}[a-zA-Z0-9/]*)" + spacePattern;
	// Define matchers.
	// 匹配空行或仅有非单词字符的行
	emptyRx = boost::regex("[^[:w:]]*");
	// 匹配一个或多个非单词字符开头，后跟随一个或多个“#”和任意字符的注释行
	commentRx = boost::regex("[^[:w:]]*#+.*");
	// 匹配行内注释
	commentInLineRx = boost::regex("([^#]+)(#.*)");
	// 匹配可能带有空白字符的数字
	numberRx = boost::regex(spacePattern + numberPattern + spacePattern);
	// 匹配数字关键字
	keyNumberValuePairRx = boost::regex(keyPattern + numberPattern + spacePattern);
	// 匹配带单位的数字关键字
	keyNumberValueUnitTripletRx = boost::regex(keyPattern + numberPattern + spacePattern + "[*]{1}" + unitPattern);
	// 匹配字符串关键字
	keyStringValuePairRx = boost::regex(keyPattern + stringPattern + spacePattern);
	// 匹配关键字和“tabular”关键字开头的行（pdf : tabular）
	tabularStartRx = boost::regex(keyPattern + "(tabular)" + spacePattern);
	// 匹配由字符串和单位组成的描述符，中间用斜杠分隔（x / eV）
	descriptorRx = boost::regex(stringPattern + spacePattern + "/" + unitPattern);
}

Properties::~Properties()
{
	//
}
// 匹配字符串"Celsius"
bool Properties::matchesCelsius(std::string str)
{
	return str == "Celsius";
}
// 匹配字符串"perCent"或"%"
bool Properties::matchesPerCent(std::string str)
{
	return (str == "perCent" || str == "%");
}
// 将一个数值转换为指定单位的值
double Properties::convert(double value, std::string unitStr)
{
	// Match empty unit string.
	// 空字符串直接返回
	if (unitStr == "")
	{
		return value;
	}
	// Try to match Celsius.
	// 匹配"Celsius"单位，将数值转为开尔文并加上标准温度和压强下的温度
	if (matchesCelsius(unitStr))
	{
		return value * G4UnitDefinition::GetValueOf("K") + CLHEP::STP_Temperature;
	}
	// Try to match %.
	// 匹配“%”单位，将数值乘以百分比常量
	if (matchesPerCent(unitStr))
	{
		return value * CLHEP::perCent;
	}
	// Match with Geant4.
	// 没有匹配到上面的单位，则使用Geant4库中函数进行单位转换
	return value * G4UnitDefinition::GetValueOf(G4String(unitStr));
}
// 解析制表符分隔的文件
void Properties::parseTabular(std::ifstream *in, std::string key)
{
	tabular tab;
	// Temporary container.
	// 储存各列名、列单位和列数据
	std::vector<std::string> columnNames;
	std::vector<std::string> columnUnits;
	std::vector<std::vector<double>> columns;
	std::string line;
	boost::match_results<std::string::const_iterator> result;
	// Match header line.
	// 读首行
	getline(*in, line);
	// Match comment at line end and cut it off.
	// 获取标题行，函数会检查并去除行尾注释
	if (boost::regex_search(line, result, commentInLineRx, boost::match_all))
	{
		line = result[1];
	}
	// Tokenizer definition.
	// 将标题行按制表符分隔成多个token
	typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
	boost::char_separator<char> sep("\t");
	// Parse the header line.
	// 解析首行，对每个token使用使用正则表达式匹配
	tokenizer headerTokens(line, sep);
	for (tokenizer::iterator it = headerTokens.begin(); it != headerTokens.end(); ++it)
	{
		std::string descriptor = (std::string)(*it);
		// Matched descriptor with unit.
		// 匹配描述符和单位，并将描述符和单位分别储存在向量中
		boost::match_results<std::string::const_iterator> regexResult;
		if (boost::regex_match(descriptor, regexResult, descriptorRx, boost::match_all))
		{
			columnNames.push_back(regexResult[1]);
			columnUnits.push_back(regexResult[2]);
		}
		else
		{
			// Matched descriptor without.
			// 没有匹配到描述符，则单位向量中对应位置储存字符串
			columnNames.push_back(descriptor);
			columnUnits.push_back("");
		}
		// 每一列在向量中初始化一个空向量
		columns.push_back(std::vector<double>());
	}
	// Exit if the header line could not be resolved.
	// 如果标题行解析失败
	if (columnNames.size() == 0)
	{
		std::cerr << "Properties::parseTabular(in, key=\"" << key << "\"): could not parse tabular " << std::endl;
		return;
	}
	// Parse table content.
	// 解析表内容
	while (getline(*in, line))
	{
		// Match comment at line end and cut it off.
		// 逐行读取文本内容，检查并移除行尾注释
		if (boost::regex_search(line, result, commentInLineRx, boost::match_all))
		{
			line = result[1];
		}
		// 将行内容按制表符分隔成多个token，
		tokenizer lineTokens(line, sep);
		// Determine number of tokens.
		// 计算token数量
		unsigned int size = 0;
		for (tokenizer::iterator it = lineTokens.begin(); it != lineTokens.end(); ++it)
		{
			size++;
		}
		// Skip if the line was empty or the number of tokens mismatches the number of columns.
		// 跳过空行或token数与列数不匹配的行
		if (line.size() == 0 || size != columns.size())
		{
			// TODO(tim): reset file position.
			break;
		}
		// Insert values.
		//
		int colId = 0;
		// 遍历每一个token
		for (tokenizer::iterator it = lineTokens.begin(); it != lineTokens.end(); ++it)
		{
			std::string cell = (std::string)(*it);
			// Match number entry.
			// 匹配数字，并将字符串转换为双精度浮点数，并调用convert转换为相应单位，储存在对应列的向量中
			if (regex_match(cell, result, numberRx))
			{
				double value = atof(cell.c_str());
				columns[colId].push_back(convert(value, columnUnits[colId]));
			}
			else
			{
				std::cerr << "Properties::parseTabular(in, key=\"" << key << "\"): could not parse cell " << cell
						  << " in line " << colId << std::endl;
			}
			colId++;
		}
	}
	// Build up table.
	// 建立表
	for (unsigned int i = 0; i < columnNames.size(); ++i)
	{
		tab[columnNames[i]] = columns[i];
	}
	// 关联关键字，并储存到entries映射中
	entries[key] = tab;
}
// 从指定文件中加载属性数据，并将数据储存在entries映射中
bool Properties::load(std::string _filename)
{
	entries.clear();
	// Open the file.
	// 尝试打开指定文件
	std::ifstream in(_filename.c_str());
	if (!in.is_open())
	{
		std::cerr << "Properties::load(filename = \"" << _filename << "\"): can not open file." << std::endl;
		return false;
	}
	// Iterate over all lines in the file.
	// 逐行读取文本内容
	std::string line;
	boost::match_results<std::string::const_iterator> result;
	while (getline(in, line))
	{
		// Match empty line.
		// 跳过空行
		if (line.empty() || boost::regex_match(line, emptyRx))
		{
			continue;
		}
		// Match comment line.
		// 跳过注释行
		if (regex_match(line, commentRx))
		{
			continue;
		}
		// Match comment at line end and cut it off.
		// 移除行尾注释
		if (boost::regex_search(line, result, commentInLineRx, boost::match_all))
		{
			line = result[1];
		}
		// Match key-number-pair.
		// 匹配关键字-数字对
		if (boost::regex_search(line, result, keyNumberValuePairRx, boost::match_all))
		{
			std::string key = result[1];
			std::string value = result[2];
			entries[key] = static_cast<double>(atof(value.c_str()));
			continue;
		}
		// Match key-number-unit-triplet.
		// 匹配关键字-数字-单位 三元组
		if (regex_search(line, result, keyNumberValueUnitTripletRx, boost::match_all))
		{
			std::string key = result[1];
			std::string value = result[2];
			std::string unit = result[3];
			entries[key] = convert(static_cast<double>(atof(value.c_str())), unit);
			continue;
		}
		// Match tabular data.
		// 匹配表格数据
		if (boost::regex_search(line, result, tabularStartRx, boost::match_all))
		{
			parseTabular(&in, result[1]);
			continue;
		}
		// Match key-string-pair.
		// 匹配关键字-字符串对
		if (boost::regex_search(line, result, keyStringValuePairRx, boost::match_all))
		{
			std::string key = result[1];
			std::string value = result[2];
			entries[key] = value;
			continue;
		}
		// Line could not be matched, so break operation with an error.
		// 无法匹配上面任意一种格式，报错
		std::cerr << "Properties::load(filename = \"" << _filename << "\"): can not match \"" << line << "\"."
				  << std::endl;
		return false;
	}
	// 匹配成功，文件名存入filename变量中
	filename = _filename;
	return true;
}
// 获取储存在entries中的数值
double Properties::getNumber(std::string key) const
{
	if (!containsNumber(key))
	{
		std::cerr << "Properties::getNumber(key = \"" << key << "\"): does not exist." << std::endl;
		return NAN;
	}
	return boost::any_cast<double>(entries.at(key));
}
// 获取储存在entries中的字符串
std::string Properties::getString(std::string key) const
{
	if (!containsString(key))
	{
		std::cerr << "Properties::getString(key = \"" << key << "\"): does not exist." << std::endl;
		return "";
	}
	return boost::any_cast<std::string>(entries.at(key));
}
// 检查数字关键字是否为存在且其值为数值
bool Properties::containsNumber(std::string key) const
{
	try
	{
		boost::any_cast<double>(entries.at(key));
		return true;
	}
	catch (...)
	{
		//
	}
	return false;
}
// 检查字符串关键字是否存在且其值为字符串
bool Properties::containsString(std::string key) const
{
	try
	{
		boost::any_cast<std::string>(entries.at(key));
		return true;
	}
	catch (...)
	{
		//
	}
	return false;
}
// 返回字符串关键字关联的表格数据
Properties::tabular Properties::getTabular(std::string key) const
{
	if (!containsTabular(key))
	{
		std::cerr << "Properties::getTabular(key = \"" << key << "\"): does not exist." << std::endl;
		return tabular();
	}
	return boost::any_cast<tabular>(entries.at(key));
}
// 将Properties中的所有属性转换为字符串形式，并返回所有属性及其值的字符串
std::string Properties::toString() const
{
	std::stringstream out;
	// Iterate over all properties.
	// 遍历所有properties属性
	for (std::map<std::string, boost::any>::const_iterator it = entries.begin(); it != entries.end(); it++)
	{
		out << it->first << ":\t";
		const std::type_info &type = it->second.type();
		if (type == typeid(double))
		{
			out << boost::any_cast<double>(it->second);
		}
		else if (type == typeid(std::string))
		{
			out << boost::any_cast<std::string>(it->second);
		}
		else if (type == typeid(tabular))
		{
			out << "tabular\n";
			tabular tab = boost::any_cast<tabular>(it->second);
			// Determine number of entries.
			// 确定表格中条目的数量
			unsigned int n = tab.begin()->second.size();
			// Print column names.
			// 打印列名
			out << "\t";
			for (tabular::const_iterator colIt = tab.begin(); colIt != tab.end(); colIt++)
			{
				out << colIt->first << "\t";
			}
			out << "\n";
			// Print values.
			// 打印每一列值
			for (unsigned int i = 0; i < n; i++)
			{
				out << "\t";
				for (tabular::const_iterator colIt = tab.begin(); colIt != tab.end(); colIt++)
				{
					boost::any entry = colIt->second.at(i);
					if (entry.type() == typeid(double))
					{
						out << boost::any_cast<double>(entry);
					}
					else if (entry.type() == typeid(std::string))
					{
						out << boost::any_cast<std::string>(entry);
					}
					out << "\t";
				}
				out << "\n";
			}
		}
		out << "\n";
	}
	return out.str();
}

void Properties::print() const
{
	std::cout << toString() << std::endl;
}
// 检查给定key是否存在且其值为tabular类型
bool Properties::containsTabular(std::string key) const
{
	try
	{
		boost::any_cast<tabular>(entries.at(key));
		return true;
	}
	catch (...)
	{
		//
	}
	return false;
}
